package com.stek.sanglhlib;

import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import android.util.Base64;

public class CryptUtil {
	private static final int SALT_BIT = 24;
	public static final int INTERATION_1024 = 1024;
	public static final int INTERATION_4096 = 4096;
	public static final int INTERATION_8192 = 8192;
	public static final String ISO_8859_1 = "ISO-8859-1";

	public static byte[] generateSalt() throws Throwable {
		SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
		byte[] salt = new byte[SALT_BIT];
		sr.nextBytes(salt);
		return salt;

	}

	public static byte[] generateSalt(int bits) throws Throwable {
		SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
		byte[] salt = new byte[bits];
		sr.nextBytes(salt);
		return salt;

	}

	// ===========================RSA===========================
	public static class RSA {
		private static final String RSA_TRANSFORMATION = "RSA";

		private static final int RSA_KEY_SIZE = 1024;

		public static KeyPair generateKey() throws Throwable {
			KeyPairGenerator keyPairGenerator = KeyPairGenerator
					.getInstance("RSA");
			keyPairGenerator.initialize(RSA_KEY_SIZE);
			KeyPair keyPair = keyPairGenerator.generateKeyPair();
			return keyPair;

		}

		public static byte[] getPrivateKeySpec(PrivateKey privateKey)
				throws Throwable {
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			PKCS8EncodedKeySpec pkcs8EncodedKeySpec = keyFactory.getKeySpec(
					privateKey, PKCS8EncodedKeySpec.class);
			return pkcs8EncodedKeySpec.getEncoded();
		}

		public static PrivateKey getPrivateKeyFromKeySpec(byte[] keySpec)
				throws Throwable {
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
					keySpec);
			return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
		}

		public static byte[] getPublicKeySpec(PublicKey publicKey)
				throws Throwable {
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			X509EncodedKeySpec x509EncodedKeySpec = keyFactory.getKeySpec(
					publicKey, X509EncodedKeySpec.class);
			return x509EncodedKeySpec.getEncoded();
		}

		public static PublicKey getPublicKeyFromKeySpec(byte[] keySpec)
				throws Throwable {
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(
					keySpec);
			return keyFactory.generatePublic(x509EncodedKeySpec);
		}

		public static byte[] encrypt(PublicKey publicKey, byte[] input)
				throws Throwable {
			Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			return cipher.doFinal(input);
		}

		public static byte[] decrypt(PrivateKey privateKey, byte[] input)
				throws Throwable {
			Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
			cipher.init(Cipher.DECRYPT_MODE, privateKey);
			return cipher.doFinal(input);
		}
	}

	// ===========================PBKDF2===========================
	public static class PBKDF2 {

		public static byte[] hash(String password, int iterationCount,
				byte[] salt) throws Throwable {
			PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), (salt),
					iterationCount, 512);
			SecretKeyFactory keyFactory = SecretKeyFactory
					.getInstance("PBKDF2WithHmacSHA1");
			return keyFactory.generateSecret(keySpec).getEncoded();

		}

		public static boolean validate(String password, byte[] storedHash,
				int iterationCount, byte[] salt) throws Throwable {
			byte[] hash = hash(password, iterationCount, salt);
			if (storedHash.length != hash.length) {
				return false;
			}
			int diff = hash.length ^ storedHash.length;
			for (int i = 0; i < hash.length && i < storedHash.length; i++) {
				diff |= hash[i] ^ storedHash[i];
			}
			return diff == 0;

		}

		public static byte[] encrypt(String password, byte[] input,
				int iterationCount, byte[] salt) throws Throwable {
			SecretKeyFactory keyFactory = SecretKeyFactory
					.getInstance("PBEWithMD5AndDES");
			SecretKey key = keyFactory.generateSecret(new PBEKeySpec(password
					.toCharArray(), (salt), iterationCount, 512));
			Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
			pbeCipher.init(Cipher.ENCRYPT_MODE, key);
			return pbeCipher.doFinal(input);

		}

		public static byte[] decrypt(String password, byte[] input,
				int iterationCount, byte[] salt) throws Throwable {
			SecretKeyFactory keyFactory = SecretKeyFactory
					.getInstance("PBEWithMD5AndDES");
			SecretKey key = keyFactory.generateSecret(new PBEKeySpec(password
					.toCharArray(), (salt), iterationCount, 512));
			Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
			pbeCipher.init(Cipher.DECRYPT_MODE, key);
			return pbeCipher.doFinal(input);
		}

	}

	// ===========================AES===========================
	public static class AES {
		private static final String AES_TRANSFORMATION = "AES/ECB/PKCS7Padding";
		public static final int AES_KEY_SIZE = 256;

		public static SecretKey generateKey() throws Throwable {
			SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
			sr.setSeed(generateSalt());
			KeyGenerator kg = KeyGenerator.getInstance("AES");
			kg.init(AES_KEY_SIZE, sr);
			return kg.generateKey();
		}

		public static byte[] encrypt(byte[] key, byte[] input) throws Throwable {
			Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
			cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
			return cipher.doFinal(input);
		}

		public static byte[] decrypt(byte[] key, byte[] input) throws Throwable {
			Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
			cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
			return cipher.doFinal(input);
		}

		public static void encryptStream(byte[] key, InputStream inputStream,
				OutputStream outputStream) throws Throwable {
			Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
			cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
			CipherOutputStream cipherOutputStream = new CipherOutputStream(
					outputStream, cipher);
			int len = 0;
			byte[] bytes = new byte[1024];
			while ((len = inputStream.read(bytes)) != -1) {
				cipherOutputStream.write(bytes, 0, len);
			}
			cipherOutputStream.flush();
			cipherOutputStream.close();
		}

		public static void decryptStream(byte[] key, InputStream inputStream,
				OutputStream outputStream) throws Throwable {
			Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
			cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
			CipherOutputStream cipherOutputStream = new CipherOutputStream(
					outputStream, cipher);
			int len = 0;
			byte[] bytes = new byte[1024];
			while ((len = inputStream.read(bytes)) != -1) {
				cipherOutputStream.write(bytes, 0, len);
			}
			cipherOutputStream.flush();
			cipherOutputStream.close();
		}
	}


	// ===========================AES===========================
	public static class BASE64Helper {
		public static byte[] decode(String input) {
			return Base64.decode(input, Base64.DEFAULT);
		}

		public static String encode(byte[] input) {
			return Base64.encodeToString(input, Base64.DEFAULT);
		}
	}

}
